<script setup lang="ts">
  import { PropType, ref, watch } from "vue";
  import ViewUrl from "@/components/ViewUrl.vue";
  const
    props = defineProps({
      modelValue: { type: String },
      list: { type: Array as PropType<Array<gt.components.Upload>>, default: [], },
      accept: { type: String, default: "image/*" },
      onPreview: { type: Function as PropType<(item: gt.components.Upload) => void>, },
      base64: { type: Boolean },
      single: { type: Boolean },
      disabled: { type: Boolean },
      upType: { type: String, default: "" },
      upOther: { type: Object }
    }),
    show = ref(false),
    url = ref(""),
    type = ref(""),
    emit = defineEmits<{
      (e: "update:modelValue", v: string): void;
      (e: "edit", v: gt.components.Upload): void;
      (e: "view", v: gt.components.Upload): void;
    }>();

  async function view(e: gt.components.Upload) {
    if (props.onPreview instanceof Function) {
      props.onPreview(e);
    } else {
      type.value = "img";
      url.value = e.url;
      show.value = true;
    }
    emit("view", e);
  }
  function del(e: gt.components.Upload) {
    const i = props.list.indexOf(e);
    if (!e.url.toLowerCase().indexOf("blob")) {
      URL.revokeObjectURL(e.url);
    }
    if (i >= 0) {
      props.list.splice(i, 1);
    }
    if (props.single) {
      emit("update:modelValue", "");
    }
  }
  async function edit(e: gt.components.Upload) {
    const i = props.list.findIndex(ee => ee == e),
      r = await chooseFile(props.accept);
    if (props.single) {
      if (r.upload) {
        emit("update:modelValue", await r.upload());
      }
    } else if (i >= 0) {
      props.list[i] = r;
    }
    emit("edit", e);
  }
  async function add() {
    if (props.disabled) {
      return;
    }
    const r = await chooseFile(props.accept);
    if (props.single) {
      if (r.upload) {
        emit("update:modelValue", await r.upload());
      }
    } else {
      props.list.push(r);
    }
    emit("edit", r);
  }
  function makeUpload(file: File): gt.components.Upload {
    const r: gt.components.Upload = {
      url: URL.createObjectURL(file), upload() {
        return new Promise<string>((resolve, reject) => {
          if (props.base64) {
            const reader = new FileReader();
            reader.onload = (e) => {
              if (e.target?.result && typeof e.target?.result == "string") {
                this.url = e.target.result;
                resolve(e.target.result);
              } else {
                // @ts-ignore
                e.err = "转化文件为base64出错";
                reject(e);
              }
            };
            reader.readAsDataURL(file);
          } else {
            api.upload(file.name, file, props.upOther || {}, props.upType).then(e => {
              URL.revokeObjectURL(this.url);
              this.url = e;
              delete this.upload;
              resolve(e);
            }).catch(reject);
          }
        });
      },
    };
    return r;
  }
  // #region 导出方法
  function chooseFile(): Promise<gt.components.Upload>;
  function chooseFile(accept: string): Promise<gt.components.Upload>;
  function chooseFile(accept: any, sum: 1): Promise<gt.components.Upload>;
  function chooseFile(accept: any, sum: number): Promise<gt.components.Upload[]>;
  function chooseFile(accept?: string, sum?: number) {
    return new Promise<gt.components.Upload | gt.components.Upload[]>((resolve, reject) => {
      const input = document.createElement("input");
      input.type = "file";
      input.className = "uploadel";
      input.accept = accept || props.accept;
      if (props.single) {
        sum = 1;
      }
      const s = parseInt(sum) || 1;
      input.multiple = s > 1;
      let h: any, rej = 1;

      input.addEventListener("change", async (e: any) => {
        if (h) { clearTimeout(h); }
        rej = 0;
        const files: FileList = e.target.files, l = files.length;
        if (l > 0) {
          if (s > 1) {
            const ret: gt.components.Upload[] = [];
            for (let i = 0; i < l; i++) {
              ret.push(makeUpload(files[i]));
            }
            resolve(ret);
          } else {
            const t = makeUpload(files[l - 1]);
            resolve(t);
          }
        } else {
          reject("选择文件数为0");
        }
        input.remove();
      });
      document.body.appendChild(input);
      input.focus();
      input.addEventListener("focus", () => {
        if (rej) {
          h = setTimeout(() => {
            input.remove();
            reject("用户取消选择文件");
          }, 500);
        }
      });
      input.click();
    });
  }

  defineExpose({ chooseFile });
  // #endregion 导出方法 结束
  watch(() => props.modelValue, n => {
    if (props.single) {
      props.list.splice(0);
      if (n) {
        props.list.push({ url: n });
      }
    }
  }, { immediate: true });
</script>
<template>
  <div class="wf pa x ox Upload">
    <div class="box xcc" v-if="!single || !list.length">
      <div class="mac-ico-add xcc" :class="disabled ? '' : 'sbss'" @click.stop="add" />
    </div>
    <div class="box" v-for="(a, ai) in list" :key="ai">
      <ViewUrl v-model="a.url" />
      <div class="mask xcc whf pa">
        <div class="mac-ico-search sbss" @click.stop="view(a)" />
        <div class="mac-ico-edit sbss ml1em" @click.stop="edit(a)" />
        <div class="mac-ico-delete sbss ml1em" @click.stop="del(a)" />
      </div>
    </div>
    <ElDialog v-if="show" class="Dialog" title="预览文件" v-model="show" append-to-body>
      <ViewUrl v-model="url" />
    </ElDialog>
  </div>
</template>
<style lang="scss" scoped>
  .Upload {
    z-index: 2;
    >.box {
      position: relative;
      width: 10em;
      height: 10em;
      border: 1px dashed var(--color-border);
      margin: 0 0.4em 0.4em 0;
      background-color: #0001;
      flex-shrink: 0;
      >.mac-ico-add {
        height: 8em;
        color: var(--color-info);
        &::before {
          font-size: 8em;
        }
      }
      >.mask {
        top: 0;
        left: 0;
        opacity: 0;
        background-color: var(--color-overlay);
        &:hover {
          opacity: 1;
        }
        >div {
          color: #fff;
        }
      }
    }
  }
</style>